﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using EnvDTE;
using EnvDTE80;
using VslangProj90;
using VSLangProj2;
using VSLangProj;

using Microsoft.VisualStudio.CommandBars;
using System.Reflection;
using System.IO;

using dotNails.CodeGen.Models;
using System.Xml.Linq;
using dotNails.CodeGen.Util;
using System.Collections;
using Microsoft.Win32;

namespace dotNails.Util {
	public class AddInUtil {
		public static string GetProjectFilePath(Project project) {
			if (IsWebProject(project)) {
				return project.Properties.Item("FullPath").Value.ToString() + "\\";
			}
			else {
				return project.FullName;
			}
		}

		public static string GetSolutionFilePath(Solution solution) {
			return solution.FullName;
		}

		public static string GetNamespaceFromFilePath(Project project, string filePath) {
			string projectFilePath = AddInUtil.GetProjectFilePath(project);
			string projectFolderPath = FileUtil.GetFolderPath(projectFilePath);
			filePath = filePath.Replace(projectFolderPath, string.Empty);
			// Lop off everything after the last backslash
			string relativeFolderPathToFile = filePath.Substring(0, filePath.LastIndexOf("\\"));
			// Replace the backslashes with dots
			relativeFolderPathToFile = relativeFolderPathToFile.Replace("\\", ".");

			return project.Name + relativeFolderPathToFile;
		}

		public static string GetRelativeFolderPath(Project project, string filePath) {
			string relativeFolderPath = string.Empty;
			string projectFilePath = AddInUtil.GetProjectFilePath(project);
			string projectFolderPath = FileUtil.GetFolderPath(projectFilePath);
			filePath = filePath.Replace(projectFolderPath, string.Empty);
			
			// Lop off everything after the last backslash, including the last backslash
			relativeFolderPath = filePath.Substring(0, filePath.LastIndexOf("\\"));
			if (relativeFolderPath.StartsWith("\\")) {
				relativeFolderPath = relativeFolderPath.Substring(1);
			}
			return relativeFolderPath;
		}

		/// <summary>
		/// Gets the file path of the specified DTE item.
		/// </summary>
		/// <param name="item">The item.</param>
		/// <returns></returns>
		public static string GetFilePath(object o) {
			string filePath = string.Empty;

			if (o is EnvDTE.Solution) {
				EnvDTE.Solution solution = (EnvDTE.Solution)o;
				filePath = solution.FullName;
			}
			else if (o is EnvDTE.Project) {
				EnvDTE.Project project = (EnvDTE.Project)o;
				if (IsWebProject(project)) {
					filePath = project.Properties.Item("FullPath").Value.ToString() + "\\";
				}
				else {
					filePath = project.FullName;
				}
			}
			else if (o is EnvDTE.ProjectItem) {
				EnvDTE.ProjectItem projectItem = (EnvDTE.ProjectItem)o;

				// A 'ProjectItem' can be a sub project, a folder (or a file)
				if (projectItem.SubProject != null) {
					filePath = GetFilePath(projectItem.SubProject);
				}
				else {
					// we return the full path of the folder (or the file)
					filePath = projectItem.get_FileNames(0);
				}
			}
			else {
				throw new System.NullReferenceException("Full path not found for the selected item");
			}

			return filePath;
		}

		public static Project GetContiningProject(object o) {
			Project project = null;

			if (o is Project) {
				project = (Project)o;
			}
			else if (o is EnvDTE.ProjectItem) {
				EnvDTE.ProjectItem projectItem = (EnvDTE.ProjectItem)o;
				project = projectItem.ContainingProject;
			}

			return project;
		}

		public static Project GetSelectedProject(DTE2 app) {
			UIHierarchy hierarchy = app.ToolWindows.SolutionExplorer;
			UIHierarchyItem item = (UIHierarchyItem)((object[])hierarchy.SelectedItems)[0];

			Project selectedProject = item.Object as EnvDTE.Project;

			return selectedProject;
		}

		public static Solution GetSelectedSolution(DTE2 app) {
			UIHierarchy hierarchy = app.ToolWindows.SolutionExplorer;
			UIHierarchyItem item = (UIHierarchyItem)((object[])hierarchy.SelectedItems)[0];

			Solution selectedSolution = item.Object as EnvDTE.Solution;

			return selectedSolution;
		}

		public static void CheckFileOutOfSourceControl(DTE2 dte, string filePath) {
			if (dte.SourceControl.IsItemUnderSCC(filePath) && !dte.SourceControl.IsItemCheckedOut(filePath)) {
				dte.SourceControl.CheckOutItem(filePath);

			}

		}

		public static List<DbmlData> GetAllDBMLsInSolution(DTE2 dte) {
			List<DbmlData> dbmls = new List<DbmlData>();

			foreach (Project project in dte.Solution.Projects) {
				foreach (ProjectItem projectItem in project.ProjectItems) {
					if (Path.GetExtension(projectItem.Name) == ".dbml") {
						dbmls.Add(new DbmlData(projectItem.get_FileNames(0), project.Name));
					}
					// Recursively check children
					GetAllDBMLsInProjectItem(dte, dbmls, projectItem, project.Name);
				}
			}

			return dbmls;
		}

		public static void GetAllDBMLsInProjectItem(DTE2 dte, List<DbmlData> dbmls, ProjectItem parentProjectItem, string projectName) {
			if (parentProjectItem.ProjectItems != null) {
				foreach (ProjectItem projectItem in parentProjectItem.ProjectItems) {
					if (Path.GetExtension(projectItem.Name) == ".dbml") {
						dbmls.Add(new DbmlData(projectItem.get_FileNames(0), projectName));
					}
				}
			}
		}

		public static ProjectItem GetProjectItemByFilePath(DTE2 dte, string filePath) {
			ProjectItem projectItem = null;
			
			foreach (Project project in dte.Solution.Projects) {
				foreach (ProjectItem item in project.ProjectItems) {
					bool getFileNamesSucceeded = false;

					try {
						// .vsmdi files do not support the get_Filenames() method.  There may be other files that do not as well.
						if (!item.Name.EndsWith(".vsmdi")) {
							if (item.get_FileNames(0) == filePath) {
								projectItem = item;
								break;
							}
							getFileNamesSucceeded = true;
						}
					}
					catch (Exception ex) {
						LogUtil.EventLogMessage("get_Filenames(0) failed for projectItem - " + item.Name, ex, ex.StackTrace);
					}

					if (getFileNamesSucceeded) {
						// Recursively check children
						projectItem = GetProjectItemByFilePathRecursive(dte, filePath, item);
						if (projectItem != null) {
							break;
						}
					}
				}
				if (projectItem != null) {
					break;
				}
			}

			return projectItem;
		}

		public static ProjectItem GetProjectItemByFilePathRecursive(DTE2 dte, string filePath, ProjectItem parentProjectItem) {
			ProjectItem projectItem = null;

			try {
				foreach (ProjectItem item in parentProjectItem.ProjectItems) {
					if (item.get_FileNames(0) == filePath) {
						projectItem = item;
						break;
					}
				}
			}
			catch (Exception ex) {
				throw new Exception(string.Format("parentProjectItem.Name was - {0}, parentProjectItem.ProjectItems was - {1}", parentProjectItem.Name, parentProjectItem.ProjectItems), ex);
			}
		
			return projectItem;
		}

		public static void RunCustomToolOnProjectItem(ProjectItem projectItem) {
			if (projectItem != null) {
				((VSProjectItem)projectItem.Object).RunCustomTool();
			}
		}

		public static void SetCustomToolOnProjectItem(ProjectItem projectItem) {
			if (projectItem != null) {
				projectItem.Properties.Item("CustomTool").Value = "dotNailsLinqToSqlGenerator";
			}
		}

		public static void AddClassFromSimpleTemplate(string className, string templateFile, Dictionary<string, string> substitutions, Project modelProject, string relativeModelFolder, string fullFolderPath, Assembly assembly) {
			// Check to see if a partial class already exists
			if (!ProjectUtil.FileExistsInProject(modelProject, relativeModelFolder, className + ".cs")) {
				// TODO: Move Embedded templates into dotNails.Templates.Simple (or Embedded) folder and namespace.  I want the embedded templates out of the root of
				//  the Templates folder so it's easier to distinguish between embedded and non-embedded templates.
				//  Create hashtable of templates found in client folder, then check hashtable for values here.  If not found, then use the embedded template, otherwise 
				//  use the client's template. Populate hashtable at beginning of unit of work, so we don't hit the filesystem for every transform.
				//  Use the same mechanism for overriding all templates (non-embedded).
				string fileContent = TemplateUtil.TransformSimpleTemplateToString("dotNails.Templates", templateFile, substitutions, assembly);

				string fullFilePath = Path.Combine(fullFolderPath, className + ".cs");
				FileUtil.CreateFileFromString(fullFilePath, fileContent);

				ProjectUtil.AddFromFile(modelProject, relativeModelFolder, fullFilePath);
			}
		}

		public static void AddFileFromString(string filename, string fileContent, string fullFolderPath, Project project, string relativeFolder) {
			string fullFilePath = Path.Combine(fullFolderPath, filename);
			FileUtil.CreateFileFromString(fullFilePath, fileContent);

			ProjectUtil.AddFromFile(project, relativeFolder, fullFilePath);
		}

		//public static ManagerData LoadManagerData(DTE2 app) {
		//    string selectedProjectName = AddInUtil.GetSelectedProjectName(app);
		//    return LoadManagerData(app, selectedProjectName, string.Empty);
		//}

		//public static ManagerData LoadManagerDataForSolution(DTE2 app) {
		//    string selectedProjectName = "Solution Items";
		//    string selectedItemName = Path.GetFileNameWithoutExtension(app.Solution.FullName);
		//    return LoadManagerData(app, selectedProjectName, selectedItemName);
		//}

		//public static ManagerData LoadManagerData(DTE2 app, string selectedProjectName, string selectedItemName) {
		//    ManagerData managerData = new ManagerData();
		//    string configFilePath = GetConfigFilePath(app, selectedProjectName, selectedItemName);

		//    XmlDocument configDoc = new XmlDocument();
			
		//    if (File.Exists(configFilePath)) {
		//        configDoc.Load(configFilePath);
		//    }
		//    else {
		//        return null;  
		//    }


		//    // Load the base namespace
		//    XmlNode namespaceNode = configDoc.SelectSingleNode("//BaseNamespace");
		//    if (namespaceNode != null) {
		//        managerData.BaseNamespace = namespaceNode.Attributes["Name"].Value;
		//    }

		//    // Load the Databases
		//    XmlNodeList databases = configDoc.SelectNodes("//Database");

		//    foreach (XmlNode databaseNode in databases) {
		//        Database database = new Database();
		//        database.Name = databaseNode.Attributes["Name"].Value;
		//        database.ConfigKey = databaseNode.Attributes["ConfigKey"].Value;
		//        if (database.ConfigKey != string.Empty) {
		//            database.ConnectionString = System.Configuration.ConfigurationManager.AppSettings[database.ConfigKey];
		//        }
		//        else {
		//            if (databaseNode.Attributes["ConnectionString"] != null) {
		//                database.ConnectionString = databaseNode.Attributes["ConnectionString"].Value;
		//            }
		//        }

		//        // Have to have a connection string either through ConfigKey or manual entry, or the database selection doesn't get saved
		//        if (database.ConnectionString != string.Empty) {
		//            managerData.Databases.Add(database);
		//        }
		//    }

		//    // Load the functional areas
		//    XmlNodeList funcAreaNodes = configDoc.SelectNodes("//FunctionalArea");

		//    foreach (XmlNode functionalAreaNode in funcAreaNodes) {
		//        FunctionalArea functionalArea = new CodeOMatic.Common.Entity.FunctionalArea();
		//        functionalArea.Name = functionalAreaNode.Attributes["Name"].Value;

		//        // Load the functional area tables
		//        string databaseName = functionalAreaNode.Attributes["Database"].Value;
		//        functionalArea.Database = managerData.Databases.FindByName(databaseName);

		//        // Fix this to only read child nodes of this node (it's returning all Tables in all Functional areas currently)
		//        XmlNodeList tables = functionalAreaNode.SelectNodes("Table");
				
		//        foreach (XmlNode tableNode in tables) {
		//            Table table = new Table();
		//            table.DatabaseName = tableNode.Attributes["DatabaseName"].Value;
		//            if (tableNode.Attributes["ObjectName"] != null) {
		//                table.ObjectName = tableNode.Attributes["ObjectName"].Value;
		//            }
		//            if (table.ObjectName == string.Empty) {
		//                table.ObjectName = table.DatabaseName;
		//            }

		//            functionalArea.Tables.Add(table);
		//        }

		//        managerData.FunctionalAreas.Add(functionalArea);
		//    }

		//    return managerData;
		//}

		//internal static void SaveManagerData(DTE2 dte, ManagerData managerData) {
		//    string selectedProjectName = AddInUtil.GetSelectedProjectName(dte);
		//    string configFilePath = GetConfigFilePath(dte, selectedProjectName, string.Empty);

		//    try {
		//        XmlWriterSettings ws = new XmlWriterSettings();
		//        ws.CheckCharacters = true;
		//        ws.CloseOutput = true;
		//        ws.Indent = true;

		//        using (XmlWriter xw = XmlWriter.Create(configFilePath, ws)) {
		//            xw.WriteStartDocument();
		//            xw.WriteStartElement("Config");

		//            // I suppose we need a separate button for saving the namespace
		//            xw.WriteStartElement("BaseNamespace");
		//            xw.WriteAttributeString("Name", managerData.BaseNamespace);
		//            xw.WriteEndElement();

		//            // Save the functional areas
		//            foreach (FunctionalArea funcArea in managerData.FunctionalAreas) {
		//                if (funcArea.Name == "[New Area]")  // Skip the dummy object
		//                    continue;

		//                xw.WriteStartElement("FunctionalArea");
		//                xw.WriteAttributeString("Name", funcArea.Name);
		//                xw.WriteAttributeString("Database", funcArea.Database.Name);

		//                foreach (Table table in funcArea.Tables) {
		//                    xw.WriteStartElement("Table");
		//                    xw.WriteAttributeString("DatabaseName", table.DatabaseName);
		//                    if (table.ObjectName != string.Empty) {
		//                        xw.WriteAttributeString("ObjectName", table.ObjectName);
		//                    }
		//                    xw.WriteEndElement();
		//                }
		//                xw.WriteEndElement();
		//            }

		//            // Save the databases
		//            foreach (Database database in managerData.Databases) {
		//                if (database.Name == "[New DB]")        // Skip the dummy object
		//                    continue;

		//                xw.WriteStartElement("Database");
		//                xw.WriteAttributeString("Name", database.Name);
		//                xw.WriteAttributeString("ConfigKey", database.ConfigKey);
		//                // Only save the ConnectionString if the ConfigKey is empty
		//                if (database.ConfigKey == string.Empty) {
		//                    xw.WriteAttributeString("ConnectionString", database.ConnectionString);
		//                }
		//                xw.WriteEndElement();
		//            }

		//            xw.WriteEndElement();
		//            xw.WriteEndDocument();
		//        }
		//    }
		//    catch (Exception ex) {
		//        string text = ex.Message;
		//    }
		//}

		//internal static void MapManagerDataToViewData(object view, ManagerData ManagerData, string functionalAreaName) {
		//    //FunctionalArea selectedFunctionalArea = ManagerData.FunctionalAreas.FindByName(functionalAreaName);

		//    //view.BaseNamespace = ManagerData.BaseNamespace;
		//    //view.ConnectionString = selectedFunctionalArea.Database.ConnectionString;
		//    //view.FunctionalAreaName = selectedFunctionalArea.Name;
		//    //view.Tables = selectedFunctionalArea.Tables;
		//    //view.DatabaseConfigKey = selectedFunctionalArea.Database.ConfigKey;
		//    //view.FunctionalArea = selectedFunctionalArea;
		//}

		//internal static void RegenerateFunctionalAreaMenu(DTE2 dte, EnvDTE.AddIn addInInstance, MenuManager menuManager, CommandBarPopup popupMenu) {
		//    menuManager.ClearPopupMenu(popupMenu, 2);

		//    ManagerData managerData = AddInUtil.LoadManagerDataForSolution(dte);
		//    int position = 2;
		//    foreach (FunctionalArea functionalArea in managerData.FunctionalAreas) {
		//        GenerateFunctionalArea generateFunctionalArea = new GenerateFunctionalArea(dte, addInInstance, functionalArea.Name);
		//        menuManager.AddCommandMenu(popupMenu, generateFunctionalArea, position);
		//        position++;
		//    }
		//}

		//public static string GetSelectedItemName(DTE2 app) {
		//    string selectedItemName = string.Empty;
		//    UIHierarchy hierarchy = app.ToolWindows.SolutionExplorer;
		//    UIHierarchyItem item = (UIHierarchyItem)((object[])hierarchy.SelectedItems)[0];

		//    Solution selectedSolution = item.Object as EnvDTE.Solution;
		//    Project selectedProject = item.Object as EnvDTE.Project;

		//    if (selectedSolution != null) {
		//        selectedItemName = Path.GetFileNameWithoutExtension(selectedSolution.FullName);
		//    }
		//    else if (selectedProject != null) {
		//        // Special Case: If the "Solution Items" project is selected when we hit save, then use the name of the solution
		//        // This avoids creating a "Solution ItemsOMatic.xml" file
		//        selectedItemName = selectedProject.Name == "Solution Items" ? Path.GetFileNameWithoutExtension(app.Solution.FullName) : selectedProject.Name;
		//    }
		//    else {
		//        selectedItemName = "";
		//    }

		//    return selectedItemName;
		//}


		//public static string GetSelectedProjectName(DTE2 app) {
		//    string selectedProjectName = string.Empty;
		//    UIHierarchy hierarchy = app.ToolWindows.SolutionExplorer;
		//    UIHierarchyItem item = (UIHierarchyItem)((object[])hierarchy.SelectedItems)[0];

		//    Solution selectedSolution = item.Object as EnvDTE.Solution;
		//    Project selectedProject = item.Object as EnvDTE.Project;

		//    if (selectedSolution != null) {
		//        selectedProjectName = "Solution Items";
		//    }
		//    else if (selectedProject != null) {
		//        selectedProjectName = selectedProject.Name;
		//    }
		//    else {
		//        selectedProjectName = "";
		//    }

		//    return selectedProjectName;
		//}

		//public static void AddConfigFile(DTE2 app, string configFilePath) {
		//    Project selectedProject = AddInUtil.GetSelectedProject(app);
		//    if (selectedProject != null) {
		//        selectedProject.ProjectItems.AddFromFile(configFilePath);
		//    }
		//    else {
		//        app.ItemOperations.AddExistingItem(configFilePath);
		//    }
		//    app.Windows.Item(Constants.vsWindowKindSolutionExplorer).Activate();
		//}

		/// <summary>
		/// Determines whether the specified project is a web project.
		/// </summary>
		/// <param name="project">The project.</param>
		/// <returns>
		/// 	<c>true</c> if the specified project is a web project, otherwise <c>false</c>.
		/// </returns>
		public static bool IsWebProject(EnvDTE.Project project) {
			if (project.Properties != null) {
				System.Collections.Specialized.StringCollection properties = new System.Collections.Specialized.StringCollection();
				foreach (EnvDTE.Property p in project.Properties) {
					properties.Add(p.Name);
				}
				foreach (EnvDTE.Property p in project.Properties) {
					if (p.Name == "OpenedURL" || p.Name == "WebApplication.OpenedURL") {
						return true;
					}
				}
			}
			return false;
		}

		/// <summary>
		/// Adds references by name to a project if they aren't present already.  Will search through assembly folders in registry,
		///  then search through folders containing existing referenced assemblies.
		/// </summary>
		/// <param name="project"></param>
		/// <param name="references"></param>
		public static void AddAssemblyReferences(Project project, List<string> references) {
			VSProject vsProject = null;

			try {
				// Try to get a reference to the VSProject
				vsProject = (VSProject)project.Object;
			}
			catch {
				return;
			}

			bool found = false;
			if (vsProject != null) {
				// Loop through all the references to add
				foreach (string reference in references) {
					// check if the reference is already present in the project
					found = HasReference(vsProject.References, reference);
					if (!found) {
						// if the reference was not found, try to add it looking into the folders specified in the registry
						found = AddReferenceFromRegistry(vsProject.References, reference);
					}
					if (!found) {
						// if the reference was not found in the folder specified in the registry,
						// look for it in other folders where current references are found
						found = AddReference(vsProject.References, reference);
					}
				}
			}
		}
		
		/// <summary>
		/// Adds a reference to a project if it isn't present already.  Will only look in the specified path for the referenced assembly.
		/// </summary>
		/// <param name="project"></param>
		/// <param name="reference"></param>
		/// <param name="path"></param>
		public static void AddAssemblyReferenceFromSpecificPath(Project project, string reference, string path) {
			VSProject vsProject = null;

			try {
				// Try to get a reference to the VSProject
				vsProject = (VSProject)project.Object;
			}
			catch {
				return;
			}

			bool found = false;
			if (vsProject != null) {
				// check if the reference is already present in the project
				found = HasReference(vsProject.References, reference);
				if (!found) {
					// if the reference was not found, add it
					AddReferenceFromSpecificPath(vsProject.References, reference, path);
				}
			}
		}

		public static void AddProjectReference(Project destinationProject, Project sourceProject) {
			VSProject vsProject = null;

			try {
				// Try to get a reference to the VSProject
				vsProject = (VSProject)destinationProject.Object;
			}
			catch {
				return;
			}

			bool found = false;
			// Don't add a reference to itself
			if (vsProject != null && destinationProject != sourceProject) {
				// check if the reference is already present in the project
				found = HasReference(vsProject.References, sourceProject);
				if (!found) {
					// if the reference was not found, add it
					vsProject.References.AddProject(sourceProject);
				}
			}
		}

		/// <summary>
		/// Look for a specified reference within a VSLangProj.References object
		/// </summary>
		/// <param name="references">Current project's references</param>
		/// <param name="referenceName">Name of the reference to search for</param>
		/// <returns>true if the reference was found, false otherwise</returns>
		private static bool HasReference(References references, string referenceName) {
			bool result = false;
			Reference existingReference;
			IEnumerator enumerator = references.GetEnumerator();
			while (enumerator.MoveNext()) {
				existingReference = (Reference)enumerator.Current;

				try {
					if (String.Compare(existingReference.Name, referenceName, true) == 0) {
						result = true;
						break;
					}

				}
				catch (Exception) {
					result = false;
					break;
				}
			}
			return result;
		}

		private static bool HasReference(References references, Project sourceProject) {
			bool result = false;
			Reference existingReference;
			IEnumerator enumerator = references.GetEnumerator();
			while (enumerator.MoveNext()) {
				existingReference = (Reference)enumerator.Current;

				try {
					if (String.Compare(existingReference.Name, sourceProject.Name, true) == 0) {
						result = true;
						break;
					}

				}
				catch (Exception) {
					result = false;
					break;
				}
			}

			return result;
		}

		/// <summary>
		/// Try to add a reference, looking into the folders specified in the registry
		/// </summary>
		/// <param name="references">Current project's references</param>
		/// <param name="referenceName">Name of the reference that should be added</param>
		/// <returns>true if the reference was found and added, false otherwise</returns>
		private static bool AddReferenceFromRegistry(References references, string referenceName) {
			bool found = false;
			RegistryKey registryKey = null;

			// Check the ASP.NET 2.0 install path first
			if (!found) {
				string aspNet2Path;
				aspNet2Path = Microsoft.Win32.Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\ASP.NET\2.0.50727.0", "Path", string.Empty).ToString();
				if (!string.IsNullOrEmpty(aspNet2Path)) {
					found = AddReferenceFromSpecificPath(references, referenceName, aspNet2Path);
				}
			}

			// Check the Local Machine section of the registry
			registryKey = Registry.LocalMachine;
			if (!found) found = AddReferenceFromRegistry(references, referenceName, registryKey, @"SOFTWARE\Microsoft\.NETFramework\AssemblyFolders");
			if (!found) {
				// Get other paths...
				registryKey = registryKey.OpenSubKey(@"SOFTWARE\Microsoft\VisualStudio");
				string[] names = registryKey.GetSubKeyNames();
				for (int i = 0; i < names.Length; i++) {
					if (!found) found = AddReferenceFromRegistry(references, referenceName, registryKey, names[i] + @"\AssemblyFolders");
				}
			}

			// Check the Current User section of the registry
			registryKey = Registry.CurrentUser;
			if (!found) found = AddReferenceFromRegistry(references, referenceName, registryKey, @"SOFTWARE\Microsoft\.NETFramework\AssemblyFolders");
			if (!found) {
				// Get other paths...
				registryKey = registryKey.OpenSubKey(@"SOFTWARE\Microsoft\VisualStudio");
				string[] names = registryKey.GetSubKeyNames();
				for (int i = 0; i < names.Length; i++) {
					if (!found) found = AddReferenceFromRegistry(references, referenceName, registryKey, names[i] + @"\AssemblyFolders");
				}
			}

			// Check the ASP.NET MVC path in registry
			if (!found) {
				string mvcPath;
				mvcPath = Microsoft.Win32.Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ASP.NET\ASP.NET MVC 1.0", "InstallPath", string.Empty).ToString();
				if (!string.IsNullOrEmpty(mvcPath)) {
					mvcPath = Path.Combine(mvcPath, "Assemblies");
					found = AddReferenceFromSpecificPath(references, referenceName, mvcPath);
				}
			}
			
			return found;
		}

		/// <summary>
		/// Try to add a reference, looking into the folders specified in the registry key
		/// </summary>
		/// <param name="references">Current project's references</param>
		/// <param name="referenceName">Name of the reference that should be added</param>
		/// <param name="source">Base registry key</param>
		/// <param name="keyName">Subkey name, containing the path where the reference should be looked for</param>
		/// <returns></returns>
		private static bool AddReferenceFromRegistry(References references, string referenceName, RegistryKey source, string keyName) {
			bool result = false;
			string path;
			RegistryKey tempKey;

			if (source != null) {
				RegistryKey key = source.OpenSubKey(keyName);
				if (key != null) {
					string[] pathnames;
					pathnames = key.GetSubKeyNames();
					if (pathnames != null) {
						for (int i = 0; i < pathnames.Length; i++) {
							tempKey = key.OpenSubKey(pathnames[i]);
							path = (string)tempKey.GetValue("", "");

							if ((path != "") && (File.Exists(path + referenceName + ".dll"))) {
								references.Add(path + referenceName + ".dll");
								result = true;
								break;
							}
						}
					}
				}
			}
			return result;
		}

		/// <summary>
		/// Try to add a reference, looking in the same folders where other references are found
		/// </summary>
		/// <param name="references">Current project's references</param>
		/// <param name="referenceName">Name of the reference that should be added</param>
		/// <returns>True if successful, false otherwise</returns>
		private static bool AddReference(References references, string referenceName) {
			bool result = false;
			Reference reference;
			IEnumerator enumerator = references.GetEnumerator();
			string path;
			while (enumerator.MoveNext()) {
				reference = (Reference)enumerator.Current;

				try {
					path = reference.Path;
					path = path.Replace(reference.Name, referenceName);
					if (File.Exists(path)) {
						references.Add(path);
						result = true;
						break;
					}
				}
				catch { }
			}

			return result;
		}

		private static bool AddReferenceFromSpecificPath(References references, string referenceName, string path) {
			bool result = false;
			string assemblyPath = Path.Combine(path, referenceName + ".dll");
			
			try {
				if (File.Exists(assemblyPath)) {
					references.Add(assemblyPath);
					result = true;
				}
			}
			catch { }

			return result;
		}


	}
}
